package com.xiaomaoguai.fcp.pre.kepler.redis.configuration;

import com.xiaomaoguai.fcp.pre.kepler.redis.cache.CacheMessageListener;
import com.xiaomaoguai.fcp.pre.kepler.redis.cache.RedisCaffeineCacheManager;
import com.xiaomaoguai.fcp.pre.kepler.redis.lock.aop.LockAspect;
import com.xiaomaoguai.fcp.pre.kepler.redis.lock.service.impl.AviatorLockKeyBuilder;
import com.xiaomaoguai.fcp.pre.kepler.redis.lock.service.impl.RedisLockServiceImpl;
import com.xiaomaoguai.fcp.pre.kepler.redis.lock.service.impl.SpringElLockKeyBuilder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.Resource;
import java.io.Serializable;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

/**
 * @author WeiHui
 * @version v1.0.0
 * @date 2019/5/24 16:28
 * @since JDK 1.8
 */
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(CacheRedisCaffeineProperties.class)
public class MyRedisAutoConfiguration {

	@Resource
	private CacheRedisCaffeineProperties cacheRedisCaffeineProperties;

	@Bean
	@ConditionalOnClass(RedisConnectionFactory.class)
	public RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
		RedisTemplate<?, ?> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		setSerializer(template);
		return template;
	}

	@Bean
	@ConditionalOnClass(RedisConnectionFactory.class)
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		setSerializer(template);
		return template;
	}

	@Bean
	public StringRedisSerializer keySerializer() {
		return new StringRedisSerializer();
	}

	@Bean
	public GenericJackson2JsonRedisSerializer valueSerializer() {
		return new GenericJackson2JsonRedisSerializer();
	}

	/**
	 * 设置序列化方式
	 *
	 * @param template redis模板
	 */
	private void setSerializer(RedisTemplate<?, ?> template) {
		template.setKeySerializer(keySerializer());
		template.setValueSerializer(valueSerializer());
		template.setHashKeySerializer(keySerializer());
		template.setHashValueSerializer(valueSerializer());
	}

	/**
	 * 初始化默认的Redis 缓存锁实现
	 *
	 * @param redisTemplate Spring StringRedisTemplate
	 * @return 默认的Redis 缓存锁实现
	 */
	@Bean
	@ConditionalOnMissingBean
	@ConditionalOnBean(RedisTemplate.class)
	public RedisLockServiceImpl redisLockService(RedisTemplate<Serializable, String> redisTemplate) {
		// 在应用启动的时候提前初始化 redis 连接池，加快第一次使用的访问速度
		String key = this.getClass().getName() + ".test";
		redisTemplate.opsForValue().setIfAbsent(key, "init redis connection", Duration.ofSeconds(10));
		return new RedisLockServiceImpl(redisTemplate, TimeUnit.SECONDS);
	}

	/**
	 * lock 注解功能
	 *
	 * @return lock切面
	 */
	@Bean
	public LockAspect lockAspect() {
		return new LockAspect();
	}

	@Bean
	public SpringElLockKeyBuilder springElLockKeyBuilder() {
		return new SpringElLockKeyBuilder();
	}

	@Bean
	public AviatorLockKeyBuilder aviatorLockKeyBuilder() {
		return new AviatorLockKeyBuilder();
	}


	@Bean
	@ConditionalOnBean(RedisTemplate.class)
	public RedisCaffeineCacheManager cacheManager(RedisTemplate<Object, Object> redisTemplate) {
		return new RedisCaffeineCacheManager(cacheRedisCaffeineProperties, redisTemplate);
	}

	@Bean
	@ConditionalOnBean(RedisTemplate.class)
	public RedisMessageListenerContainer redisMessageListenerContainer(RedisTemplate<Object, Object> redisTemplate, RedisCaffeineCacheManager redisCaffeineCacheManager) {
		RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
		redisMessageListenerContainer.setConnectionFactory(redisTemplate.getConnectionFactory());
		CacheMessageListener cacheMessageListener = new CacheMessageListener(redisTemplate, redisCaffeineCacheManager);
		redisMessageListenerContainer.addMessageListener(cacheMessageListener, new ChannelTopic(cacheRedisCaffeineProperties.getRedis().getTopic()));
		return redisMessageListenerContainer;
	}

}
